using System;
using System.Collections.Generic;
using System.Text;
using SlimDX;
using System.Globalization;

namespace Barrage.Global
{
    /// <summary>
    /// Helper class useful for vector math and some common functions
    /// </summary>
    public static class GameMath
    {
        /// <summary>
        /// Returns the value for a quadratic interpolation between 0 and 1
        /// </summary>
        /// <param name="x">Interpolation fraction</param>
        /// <returns>Value for a quadratic interpolation between 0 and 1</returns>
        public static float QuadraticInterpolation(float x)
        {
            return 3 * x * x - 2 * x * x * x;
        }        
        /// <summary>
        /// Dampens the current value to the desired one, resulting in a smooth variation instead of a direct change.
        /// Applies the formula:
        /// Current = From * Smoothness + To * (1- Smoothness)
        /// Not recommended for time-based operations, as it's frame-rate dependant.
        /// </summary>
        /// <param name="from">Current value</param>
        /// <param name="to">Desired value</param>
        /// <param name="smoothness">Smoothness, from 0 to 1: 0 means direct step, 1 means never change</param>
        /// <returns>Damped value</returns>
        public static float Damping(float from, float to, float smoothness)
        {
            return from * smoothness + to * (1 - smoothness);
        }
        /// <summary>
        /// Non-time dependant damping
        /// </summary>
        /// <param name="from">Current value</param>
        /// <param name="to">Desired value</param>
        /// <param name="smoothness">Smoothness (between 0 and 1): Desired percentage applied per second</param>
        /// <param name="timeInterval">Elapsed time in seconds</param>
        /// <returns>Damped value</returns>
        public static float Damping(float from, float to, float smoothness, float timeInterval)
        {
            float g = (float)Math.Pow(smoothness, timeInterval);
            return from * g + to * (1 - g);
        }

        /// <summary>
        /// Non-time dependant damping
        /// </summary>
        /// <param name="from">Current value</param>
        /// <param name="to">Desired value</param>
        /// <param name="smoothness">Smoothness (between 0 and 1): Desired percentage applied per second</param>
        /// <param name="timeInterval">Elapsed time in seconds</param>
        /// <returns>Damped value</returns>
        public static Vector3 Damping(Vector3 from, Vector3 to, float smoothness, float timeInterval)
        {
            float g = (float)Math.Pow(smoothness, timeInterval);

            return from * g + to * (1 - g);
        }
        
        /// <summary>
        /// Non-time dependant damping
        /// </summary>
        /// <param name="from">Current value</param>
        /// <param name="to">Desired value</param>
        /// <param name="smoothness">Smoothness (between 0 and 1): Desired percentage applied per second</param>
        /// <param name="timeInterval">Elapsed time in seconds</param>
        /// <returns></returns>
        public static Vector3 Damping(Vector3 from, Vector3 to, Vector3 smoothness, float timeInterval)
        {
            Vector3 v=Vector3.Zero;
            v.X=Damping(from.X,to.X,smoothness.X,timeInterval);
            v.Y=Damping(from.Y,to.Y,smoothness.Y,timeInterval);
            v.Z=Damping(from.Z,to.Z,smoothness.Z,timeInterval);
            return v;
        }
        /// <summary>
        /// Returns one normal of the line represented by two points
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public static Vector2 Normal(Vector2 from, Vector2 to)
        {
            Vector2 line=to-from;
		    float x=line.X;float y=line.Y;
			Vector2 normal=new Vector2();
			if (x!=0) 
			{
				normal.X=-y/x;
				normal.Y=1/(float)Math.Sqrt(normal.X*normal.X+1);
				normal.Normalize();
			}
			else if (y!=0) 
			{
				normal.Y=0;
				normal.X=(from.Y>to.Y)?1:-1;					
			}
			else 
			{
				normal.X=1;
				normal.Y=0;
			}
			if (x<0) 
			{
				normal*=-1;
			}
			return normal;
		}

        /// <summary>
        /// Parses two strings into a vector value
        /// </summary>
        /// <param name="x">X component string</param>
        /// <param name="y">Y component string</param>
        /// <returns>Resulting vector, or [0,0] if it couldn't be parsed</returns>
        public static Vector2 ParseVector2(string x, string y)
        {
            return ParseVector2(x, y, Vector2.Zero);
        }

        /// <summary>
        /// Parses two strings into a vector value
        /// </summary>
        /// <param name="x">X component string</param>
        /// <param name="y">Y component string</param>
        /// <param name="defaultValues">Default coordinate values</param>
        /// <returns>Resulting vector, or default values if it couldn't be parsed</returns>
        public static Vector2 ParseVector2(string x, string y,Vector2 defaultValues)
        {
            Vector2 value = defaultValues;
            if (x != null)
            {
                float vX;
                if (Single.TryParse(x, NumberStyles.Float, NumberFormatInfo.InvariantInfo,out vX)) value.X = vX;
            }
            if (y != null)
            {
                float vY;
                if (Single.TryParse(y, NumberStyles.Float, NumberFormatInfo.InvariantInfo,out vY)) value.Y=vY;
            }
            return value;
        }

        /// <summary>
        /// Parses three strings into a vector value
        /// </summary>
        /// <param name="x">X component string</param>
        /// <param name="y">Y component string</param>
        /// <param name="z">Z component string</param>
        /// <returns>Resulting vector, or [0,0,0] if it couldn't be parsed</returns>
        public static Vector3 ParseVector3(string x, string y, string z)
        {
            return ParseVector3(x, y, z, Vector3.Zero);
        }

        /// <summary>
        /// Parses three strings into a vector value
        /// </summary>
        /// <param name="x">X component string</param>
        /// <param name="y">Y component string</param>
        /// <param name="z">Z component string</param>
        /// <param name="defaultValues">Default coordinate values</param>
        /// <returns>Resulting vector, or default values if it couldn't be parsed</returns>
        public static Vector3 ParseVector3(string x, string y, string z, Vector3 defaultValues)
        {
            Vector3 value = defaultValues;
            if (x != null)
            {
                float vX;
                if (Single.TryParse(x, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out vX)) value.X = vX;
            }
            if (y != null)
            {
                float vY;
                if (Single.TryParse(y, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out vY)) value.Y = vY;
            }
            if (z != null)
            {
                float vZ;
                if (Single.TryParse(z, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out vZ)) value.Z = vZ;
            }
            return value;
        }

        /// <summary>
        /// Returns the max component value of the vector
        /// </summary>
        /// <param name="v">Vector to be checked</param>
        /// <returns>Max component value</returns>
        public static float VectorMaxValue(Vector3 v)
        {
            return Math.Max(v.X, Math.Max(v.Y, v.Z));
        }

        /// <summary>
        /// Returns the min component value of the vector
        /// </summary>
        /// <param name="v">Vector to be checked</param>
        /// <returns>Min component value</returns>
        public static float VectorMinValue(Vector3 v)
        {
            return Math.Min(v.X, Math.Min(v.Y, v.Z));
        }

        /// <summary>
        /// Returns a vector formed by each component's min value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Min values vector</returns>        
        public static Vector2 VectorMin(Vector2 v1, Vector2 v2)
        {
            return new Vector2(Math.Min(v1.X, v2.X), Math.Min(v1.Y, v2.Y));
        }

        /// <summary>
        /// Returns a vector formed by each component's min value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Min values vector</returns>        
        public static Vector3 VectorMin(Vector3 v1, Vector3 v2)
        {
            return new Vector3(Math.Max(v1.X, v2.X), Math.Min(v1.Y, v2.Y), Math.Min(v1.Z, v2.Z));
        }

        /// <summary>
        /// Returns a vector formed by each component's min value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Min values vector</returns>        
        public static Vector4 VectorMin(Vector4 v1, Vector4 v2)
        {
            return new Vector4(Math.Min(v1.X, v2.X), Math.Min(v1.Y, v2.Y), Math.Min(v1.Z, v2.Z), Math.Min(v1.W, v2.W));
        }

        /// <summary>
        /// Returns a vector formed by each component's max value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Max values vector</returns>        
        public static Vector2 VectorMax(Vector2 v1, Vector2 v2)
        {
            return new Vector2(Math.Max(v1.X,v2.X), Math.Max(v1.Y,v2.Y));
        }

        /// <summary>
        /// Returns a vector formed by each component's max value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Max values vector</returns>        
        public static Vector3 VectorMax(Vector3 v1, Vector3 v2)
        {
            return new Vector3(Math.Max(v1.X, v2.X), Math.Max(v1.Y, v2.Y),Math.Max(v1.Z,v2.Z));
        }

        /// <summary>
        /// Returns a vector formed by each component's max value
        /// </summary>
        /// <param name="v1">Vector to be checked</param>
        /// <param name="v2">Vector to be checked</param>
        /// <returns>Max values vector</returns>        
        public static Vector4 VectorMax(Vector4 v1, Vector4 v2)
        {
            return new Vector4(Math.Max(v1.X,v2.X), Math.Max(v1.Y,v2.Y),Math.Max(v1.Z,v2.Z),Math.Max(v1.W,v2.W));
        }

        /// <summary>
        /// Returns a vector formed by each component's absolute value
        /// </summary>
        /// <param name="v">Vector to be checked</param>
        /// <returns>Absolute values vector</returns>        
        public static Vector2 VectorAbs(Vector2 v)
        {
            return new Vector2(Math.Abs(v.X), Math.Abs(v.Y));
        }

        /// <summary>
        /// Returns a vector formed by each component's absolute value
        /// </summary>
        /// <param name="v">Vector to be checked</param>
        /// <returns>Absolute values vector</returns>        
        public static Vector3 VectorAbs(Vector3 v)
        {
            return new Vector3(Math.Abs(v.X), Math.Abs(v.Y),Math.Abs(v.Z));
        }

        /// <summary>
        /// Returns a vector formed by each component's absolute value
        /// </summary>
        /// <param name="v">Vector to be checked</param>
        /// <returns>Absolute values vector</returns>        
        public static Vector4 VectorAbs(Vector4 v)
        {
            return new Vector4(Math.Abs(v.X), Math.Abs(v.Y), Math.Abs(v.Z), Math.Abs(v.W));
        }

        /// <summary>
        /// Returns a color in hexadecimal format, starting with a slash character or not
        /// </summary>
        /// <param name="text">Text string</param>
        /// <returns>Parsed color, or black if error found</returns>        
        public static Color4 ParseColor(string text)
        {
            return ParseColor(text, new Color4(0));
        }

        /// <summary>
        /// Returns a color in hexadecimal format, starting with a slash character or not
        /// </summary>
        /// <param name="text">Text string</param>
        /// <param name="defaultColor">Default color value</param>
        /// <returns>Parsed color, or default color if error found</returns>        
        public static Color4 ParseColor(string text, Color4 defaultColor)
        {
            int result=0;
            if (text.Length == 0) return defaultColor;
            if (text[0] == '#') {
                if (int.TryParse(text.Substring(1), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo,out result))
                    return new Color4(result);
            } else {
                if (int.TryParse(text, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo,out result))
                    return new Color4(result);
                
            }
            return defaultColor;
        }

        /// <summary>
        /// Divides each vector component by the W value
        /// </summary>
        /// <param name="v">Vector to be unprojected</param>
        /// <returns>Vector with values divided by W</returns>
        public static Vector4 UnprojectVector(Vector4 v)
        {
            return v / v.W;
        }

        static Random random = new Random();
        /// <summary>
        /// Returns a fully opaque random color , each component ranging from 0 to 1
        /// </summary>
        /// <returns>A fully opaque random color</returns>
        public static Color4 RandomColor()
        {
            return new Color4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());
        }
    }
}
